package com.flashflexpro.flexdirector.agent{
    import com.flashflexpro.flexdirector.agent.console.Console;

    import com.flashflexpro.flexdirector.agent.view.ViewHandler;

    import com.flashflexpro.flexdirector.conn.RemoteCallingEvent;

    import flash.display.DisplayObject;
    import flash.events.EventDispatcher;
    import flash.events.EventDispatcher;

    import mx.automation.Automation;
    import mx.automation.AutomationID;
    import mx.automation.AutomationManager;
    import mx.automation.IAutomationClass;
    import mx.automation.IAutomationEventDescriptor;
    import mx.automation.IAutomationMethodDescriptor;
    import mx.automation.IAutomationObject;
    import mx.automation.events.AutomationRecordEvent;
    import mx.managers.SystemManager;


    public class FlexDirectorAgent extends EventDispatcher{

        private static var _inst:FlexDirectorAgent;
        public static function get inst():FlexDirectorAgent{
            return _inst;
        }

        private static var _sysManager:SystemManager;
        public static function get sysManager():SystemManager{
            return _sysManager;
        }

        //will be called by agentHalo or sparkHalo
        public static function init( root:SystemManager, bi:XML ):void{
            _sysManager = root;

            var serverPort:int = 9696;
            var serverAddress:String = "127.0.0.1";

            //connect to console
            var appInstId:String = root.loaderInfo.parameters[ RemoteCallingEvent.FLEX_DIRECTOR_CONNECTION_ID ];
            if( appInstId == null ){
                appInstId = RemoteCallingEvent.TEMP_APP_INST_PRIFEX + new Date().getTime();
            }
            //connect to console
            var paramServer:String = root.loaderInfo.parameters[ RemoteCallingEvent.FLEX_DIRECTOR_SERVER ];
            if( paramServer == null ){
                CONFIG::debugging{
                    trace( "FlexDirectorAdapter.init flexdirectorServer not found, using 127.0.0.1:8964" );
                }
            }
            else{
                if( paramServer.indexOf( ":" ) > 6 ){
                    var paramServerArr:Array = paramServer.split( ":" );
                    serverAddress = paramServerArr[ 0 ];
                    serverPort = int( paramServerArr[ 1 ] );
                }
                else{
                    serverAddress = paramServer;
                }

                if( !RemoteCallingEvent.IP_ADDRESS_REG_EXP.test( serverAddress ) || ( serverPort < 256 || serverPort > 60000 ) ){
                    throw new Error( "Server Address: " + paramServer + " wrong!" )
                }
            }

            _buildInfo = bi;

            _inst = new FlexDirectorAgent();

            AutomationManager.init( root as DisplayObject );
            Automation.restrictionNeeded = false;


            /**
             * DO NOT try to split recording and replaying, because it's very possible
             * that user need to record after replaying some actions.
             * BUT todo:// should this lazy initialize?
             */
            new ViewHandler( root );
            new Console( appInstId, root, serverAddress, serverPort );
        }

        public static function stopRecording():void{

            try{
                Automation.automationManager.incrementCacheCounter();
                Automation.automationManager.removeEventListener( AutomationRecordEvent.RECORD, onAutoRecord );
                Automation.automationManager.endRecording();
                Automation.automationManager.decrementCacheCounter()
            }
            catch( e:Error ){
                trace( e.getStackTrace() );
            }
        }

        public static function startRecording():void{
            try{
                Automation.automationManager.incrementCacheCounter();
                Automation.automationManager.addEventListener( AutomationRecordEvent.RECORD, onAutoRecord, false, 0,
                        true );
                Automation.automationManager.beginRecording();
                Automation.automationManager.decrementCacheCounter()
            }
            catch( e:Error ){
                trace( e.getStackTrace() );
            }
        }


        private static function onAutoRecord( event:AutomationRecordEvent ):void{
            Automation.automationManager.incrementCacheCounter();
            try{
                _inst.dispatchEvent( event );
            }
            catch ( e:Error ){
                trace( e.getStackTrace() );
            }

            Automation.automationManager.decrementCacheCounter()
        }

        public static const ID_NOT_PARSABLE:String = "idNotParsable";
        public static const TARGET_NOT_FOUND:String = "targetNotFound";
        public static const TARGET_NOT_SYNC:String = "targetNotSync";
        public static const TARGET_NOT_VISIBLE:String = "targetNotVisible";

        public function findAutoObjByAutoIdStr( aid:AutomationID,
                                                makeSureObjAvailable:Boolean = true ):IAutomationObject{
            var target:IAutomationObject = null;
            try{
                target = Automation.automationManager.resolveIDToSingleObject( aid );
            }
            catch( e:Error ){
                if( aid != null ){
                    aid.removeLast();
                    while( aid.length > 0 ){
                        target = Automation.automationManager.resolveIDToSingleObject( aid );
                        if( target != null ){
                            break;
                        }
                    }
                    throw new Error( TARGET_NOT_FOUND, aid.toString() );
                }
                else{
                    throw new Error( ID_NOT_PARSABLE );
                }
            }
            if( makeSureObjAvailable ){
                if( !Automation.automationManager.isSynchronized( target ) ){
                    throw new Error( TARGET_NOT_SYNC );
                }
                if( !Automation.automationManager.isVisible( target as DisplayObject ) ){
                    throw new Error( TARGET_NOT_VISIBLE );
                }
            }
            return target;

        }

        public static function replayStep( target:IAutomationObject, name:String, args:Array ):Object{
            Automation.automationManager.incrementCacheCounter();
            var rt:Object;
            var error:String;
            try{
                var automationClass:IAutomationClass = Automation.automationManager.automationEnvironment
                        .getAutomationClassByInstance( target );
                var methodDescriptor:IAutomationMethodDescriptor = automationClass.getDescriptorForMethodByName( name );
                if( methodDescriptor != null ){
                    rt = methodDescriptor.replay( target, args );
                }
                else{
                    var eventDescriptor:IAutomationEventDescriptor = automationClass
                            .getDescriptorForEventByName( name );
                    if( eventDescriptor != null ){
                        try{
                            rt = eventDescriptor.replay( target, args );
                        }
                        catch( er:Error ){
                            trace( er.getStackTrace() );
                        }
                    }
                }
            }
            catch( ee:Error ){
                error = ee.message;
            }
            Automation.automationManager.decrementCacheCounter();
            if( error != null ){
                throw new Error( error );
            }
            return { value:rt, type:null};
        }


        private static var _buildInfo:XML;

        public static function getBuildInfo():XML{
            return _buildInfo;
        }
    }
}